import json
import math


class KeFloorplanConverter:
    """
    贝壳户型图yolo坐标转换mango坐标
    """

    def __init__(self):
        # 贝壳图片宽高，固定大小
        self.keImagesSize = [1000, 750]

        # 芒果除以贝壳长度倍数
        # a = 346px
        # b = 266px
        # c = (6189+6309)/2 = 6249mm
        # d = 630mm/2
        # (a/b)*(c+d)/a = (346px/266px)/(6249+315)mm/346px =  (346/266)/6564/346 =  24.6767 mm/px
        
        # self.mangoDivideKe = 30
        self.mangoDivideKe = 1
        # self.mangoDivideKe = 1

        # 芒果户型图绘制边计数器
        self.mangoDrawingEdge = []
        # mango户型图drawingjson
        self.mangoDict = {
            "code": 1,
            "msg": "成功",
            "data": {
                "compassAngle": "0",
                "platform": "3",
                "drawing": {
                    "name": "",
                    "floor": {
                        "base": 0,
                        "list": [
                            {
                                "name": "1F",
                                "level": 0,
                                "areaMode": "inner",
                                "floorplanRotation": 0,
                                "bitmap": {
                                    "position": [
                                        0,
                                        0
                                    ],
                                    "rotation": 0
                                },
                                "drawing": {
                                    "cornerList": [],
                                    "wallList": [],
                                    "roomList": [],
                                    "hollowList": [],
                                    "pitList": [],
                                    "stuffList": [],
                                    "labelTextList": [],
                                    "labelLineList": [],
                                    "labelImageList": [],
                                    "brushPointList": [],
                                    "brushLineList": [],
                                    "brushCurveList": [],
                                    "legengTagList": [],
                                    "compass": {
                                        "rotation": 0
                                    }
                                },
                                "cornerList": [],
                                "wallList": [],
                                "roomList": [],
                                "hollowList": [],
                                "pitList": [],
                                "stuffList": [],
                                "labelTextList": [],
                                "labelLineList": [],
                                "brushPointList": [],
                                "brushLineList": [],
                                "brushCurveList": [],
                                "legengTagList": []
                            }
                        ],
                        "information": {
                            "titleBar": {}
                        }
                    }
                }
            }
        }

    def run(self, yolo: list, extracted_values):
        """
        入口
        """
        walls = []
        hollows = []
        for item in yolo:
            if int(item[0]) == 0:
                walls.append(item)
            else:
                hollows.append(item)
                # self.wallToMangoJson(list(map(int, item))) 

        cornerList,wallList = self.convert_drawing(walls)
        self.wallToFloorJson(cornerList,wallList)
        self.hollowToFloorJson(hollows, cornerList, wallList)
        self.get_rooms_data(extracted_values)
        return self.mangoDict

    def hollowToFloorJson(self, hollowList, cornerList, wallList):
        """
        空洞（门窗）
        """
        drawing = self.mangoDict["data"]["drawing"]["floor"]["list"][0]
        for item in hollowList:
            code = ""
            resource = 0
            # 3# single_doors是singleDoor代码100
            if int(item[0]) == 1:
                code = "singleDoor"
                resource = 100
            # 1 casement_windows是basicWindow代码103
            elif int(item[0]) == 3:
                code = "basicWindow"
                resource = 103
            # 5 entry_doors是doubleDoor代码101
            elif int(item[0]) == 5:
                code = "doubleDoor"
                resource = 101
            # 4 sliding_doors是slidingDoor代码102
            elif int(item[0]) == 4:
                code = "slidingDoor"
                resource = 102
            # 2 scoop_windows是boilerWindow代码是104
            elif int(item[0]) == 2:
                code = "boilerWindow"
                resource = 104

            # 线条转换为墙体 坐标与墙的比例因子是20：
            scale_factor = self.mangoDivideKe
            # 非墙的起点
            start_corner1 = (item[1], item[2])
            start_corner = (item[1] * scale_factor, item[2] * scale_factor)
            start_corner_middle = (
                (item[1] + item[3]) / 2 * scale_factor,
                (item[2] + item[4]) / 2 * scale_factor,
            )
            # print("非墙的起点start_corner1:", start_corner1)
            # print("非墙的起点start_corner:", start_corner)
            # print("非墙的起点start_corner_middle:", start_corner_middle)
            # 非墙的终点
            end_corner1 = (item[3], item[4])
            end_corner = (item[3] * scale_factor, item[4] * scale_factor)
            end_corner_middle = (
                (item[1] + item[3]) / 2 * scale_factor,
                (item[2] + item[4]) / 2 * scale_factor,
            )
            # print("非墙的终点end_corner1:", end_corner1)
            # print("非墙的终点end_corner:", end_corner)
            # print("非墙的终点end_corner_middle", end_corner_middle)

            # 找到在这些起点终点之间的墙段
            wall_index = -1
            offset = 0
            for idx, wall in enumerate(wallList):
                door_isstart = -1
                door_isleft = -1
                if code == "singleDoor":
                    door_isstart = item[5][0]
                    door_isleft = item[5][1]

                # 获取当前墙体段的起点和终点坐标。
                wall_start = cornerList[wall[0]]
                # print("wall_start:", wall_start)
                wall_end = cornerList[wall[1]]
                start_end_ischange = False
                # 处理起点和终点
                if wall_start[0]==wall_end[0] and wall_end[1]<wall_start[1]:
                    start_end_ischange = True
                    wall_start = cornerList[wall[1]]
                    wall_end = cornerList[wall[0]]
                    pass
                if wall_start[1]==wall_end[1] and wall_end[0]<wall_start[0]:
                    start_end_ischange = True
                    wall_start = cornerList[wall[1]]
                    wall_end = cornerList[wall[0]]
                    pass

                if door_isleft==0:
                    _wall_start = wall_start
                    _wall_end = wall_end
                    wall_start = _wall_end
                    wall_end = _wall_start

                # print(f"wall_start:{wall_start},wall_end:{wall_end}")
                # print("wall_end:", wall_end)
                # 检查起点和终点是否都在当前墙体段上。is_point_on_line_segment 方法用于判断一个点是否在一条线段上。
                if self.is_point_on_line_segment(
                    start_corner_middle, wall_start, wall_end
                ) and self.is_point_on_line_segment(
                    end_corner_middle, wall_start, wall_end
                ):
                    # print("满足条件的起点:", start_corner)
                    # print("满足条件的终点:", end_corner)
                    # print("符合条件的墙体段:", wall)
                    wall_index = idx
                    # print("wall_index:", wall_index)
                    # 计算起点到墙体段起点的距离并赋值给offset
                    offset = self.distance(wall_start, end_corner_middle)
                    # print("起点到墙体段起点的距离:", offset)
                    break

            # 根据实际需求定义每个门窗的详细信息
            # TODO：门的朝向问题
            # surface 门开的方向，朝上开，门往左靠；门开的方向，朝下开，门往右靠
            # fliped，翻转门，True为翻转，False为不翻转

            surface = [wallList[wall_index][0], wallList[wall_index][1]] if start_end_ischange == False else [wallList[wall_index][1], wallList[wall_index][0]]
            fliped = False

            if code == "singleDoor":
                # 门开的方向，朝上开，门往左靠 door_isleft=1,door_isstart=1                
                door_isleft = item[5][1] 
                door_isstart = item[5][0]
                if door_isstart == 1:
                    fliped = False
                else:
                    fliped = True
                pass
                if door_isleft == 0:
                    surface = [wallList[wall_index][1], wallList[wall_index][0]] if start_end_ischange == False else [wallList[wall_index][0], wallList[wall_index][1]]                   
                else:
                    surface = [wallList[wall_index][0], wallList[wall_index][1]] if start_end_ischange == False else [wallList[wall_index][1], wallList[wall_index][0]]
                pass
             

            hollow_detail = {
                # "width":  (
                #     800
                #     if code in ["singleDoor"]
                #     else get_line_width(item)
                #     if code in ["basicWindow", "boilerWindow"]
                #     else 1500
                # )
                "width": 800 if code in ["singleDoor"] else self.get_line_width(item) if code in ["basicWindow", "boilerWindow"] else 1500,
                "sillThickness": 200,
                "fliped": fliped,
                "offset": offset,
                "isVirtual": False,
                "surface": surface,
            }

            drawing["drawing"]["hollowList"].append(hollow_detail)
            drawing["hollowList"].append(
                {"code": code, "resource": resource, "entrance": None}
            )
    
    def get_rooms_data(self, extracted_values):
        from shapely.geometry import Point, LineString
        from shapely.ops import polygonize, unary_union

        drawing = self.mangoDict["data"]["drawing"]["floor"]["list"][0]

        # 你的数据
        cornerList = drawing["drawing"]["cornerList"]
        wallList =  drawing["drawing"]["wallList"]

        # 设置墙体厚度的一半，单位：毫米
        half_wall_thickness = 100  # 例如 100 毫米

        # 创建一个列表来保存所有的线段（墙壁）
        lines = []
        for wall in wallList:
            point1 = cornerList[wall[0]]
            point2 = cornerList[wall[1]]
            line = LineString([point1, point2])
            lines.append(line)

        # 使用 shapely 的 unary_union 函数来合并所有线段，并确保封闭性
        merged_lines = unary_union(lines)

        # 使用 polygonize 函数来构建多边形
        polygons = list(polygonize(merged_lines))

        # 房间列表
        roomList = []

        # 房间信息列表
        roomInfoList = []

        # 遍历每个多边形
        for polygon in polygons:
            room_corners = []
            for coord in polygon.exterior.coords[:-1]:  # 排除最后一个点（与第一个点相同）
                point = Point(coord)
                if [point.x, point.y] in cornerList:
                    index = cornerList.index([point.x, point.y])
                    room_corners.append(index)
            
            # 计算房间的中心点作为标签位置
            label_position = [polygon.centroid.x, polygon.centroid.y]
            
            # # 计算房间的外面积，并将其转换为平方米
            outer_area = polygon.area / 1e6  # 转换为平方米

            # # 计算房间的内面积，使用墙体厚度的一半
            inner_polygon = polygon.buffer(-half_wall_thickness)
            inner_area = inner_polygon.area / 1e6  # 转换为平方米
            
            roomList.append({"cornerList": room_corners})
            room_name = "未命名"
            room_floor = "defaultFloor"
            for _item in extracted_values:
                _point = Point(_item["point"])
                if polygon.contains(_point):
                    room_name = _item["name"].split('_')[0]
                    room_floor = _item["name"].split('_')[1]
                    break
            roomInfoList.append({
                "type": room_name,
                "name": room_name,
                # "innerArea": round(inner_area, 2),
                "outerArea": round(outer_area, 2),
                # "customArea": round(inner_area, 2),  # 假设自定义面积和内面积相同
                "floor": room_floor,
                "labelPosition": label_position
            })

        # 找到所有的孤立角点
        all_room_corners = set([corner for room in roomList for corner in room['cornerList']])
        isolated_corners = [corner for corner in range(len(cornerList)) if corner not in all_room_corners]

        # 将孤立角点添加到正确的房间中
        remove_room_list = set()
        for corner in isolated_corners:
            point = Point(cornerList[corner])
            for i, polygon in enumerate(polygons):
                if polygon.contains(point):
                    # 找到孤立点的墙体另一端
                    other_corner = ()
                    for wall in wallList:
                        if wall[0] == corner or wall[1] == corner:
                            other_corner = wall[1] if wall[0] == corner else wall[0]
                            break
                    
                    # 找到插入位置
                    if other_corner in roomList[i]['cornerList']:
                        insert_position = roomList[i]['cornerList'].index(other_corner) + 1
                        roomList[i]['cornerList'].insert(insert_position, corner)
                        roomList[i]['cornerList'].insert(insert_position+1, other_corner)
                        break
                    else:
                        remove_room_list.add(i)

        roomList = [line for idx, line in enumerate(roomList) if idx not in remove_room_list]
        roomInfoList = [line for idx, line in enumerate(roomInfoList) if idx not in remove_room_list]


        # print(f"房间数量: {len(roomList)}")
        # print("roomList:", roomList)
        # print("roomInfoList:", roomInfoList)

        # 按 innerArea 排序
        # sorted_rooms = sorted(roomInfoList, key=lambda x: x["outerArea"])

        # # 更新名称        
        # if len(sorted_rooms) > 2:
        #     sorted_rooms[0]["name"] = "卫生间"
        #     sorted_rooms[0]["floor"] = "1002"
        #     sorted_rooms[-2]["name"] = "主卧"
        #     sorted_rooms[-2]["floor"] = "1000"
        #     sorted_rooms[-1]["name"] = "客厅"
        #     sorted_rooms[-1]["floor"] = "1001"



        # # 将更新后的名称应用回原来的列表
        # for room in roomInfoList:
        #     for sorted_room in sorted_rooms:
        #         if room["outerArea"] == sorted_room["outerArea"]:
        #             room["name"] = sorted_room["name"]
        #             room["type"] = sorted_room["name"]
        #             room["floor"] = sorted_room["floor"]
        #             break
        drawing["drawing"]["roomList"] = roomList
        drawing["roomList"] = roomInfoList

        
        


    def get_line_width(self,item):
        width = 800
        if(item[1] == item[3]):
            width = item[4]-item[2]
            pass
        else:
            width = item[3]-item[1]
            pass
        return width

    def wallToMangoJson(self, wall: list):
        """
        墙
        """
        # x_min, y_max, x_max, y_min = self.xywhn_to_xyxy(wall[1], wall[2], wall[3], wall[4], self.keImagesSize[0], self.keImagesSize[1])
        # x1, y1, x2, y2, h = self.xyxy_to_mango(x_min, y_max, x_max, y_min)

        t, x1, y1, x2, y2 = map(lambda x: x * self.mangoDivideKe, wall)
        h = 200

        drawing = self.mangoDict["data"]["drawing"]["floor"]["list"][0]
        # 资源类型
        drawing["wallList"].append({"resource": 2, "code": "bearing"})

        # 点
        index_start, isNewPoint = self.getPointIndex(f"{x1},{y1}")
        if isNewPoint:
            drawing["drawing"]["cornerList"].append([x1, y1])
        index_end, isNewPoint = self.getPointIndex(f"{x2},{y2}")
        if isNewPoint:
            drawing["drawing"]["cornerList"].append([x2, y2])

        # 边
        drawing["drawing"]["wallList"].append([index_start, index_end, h])

    def wallToFloorJson(self, cornerList, wallList):
        """
        墙
        """
        drawing = self.mangoDict["data"]["drawing"]["floor"]["list"][0]
        # 边
        for item in wallList:
            drawing["drawing"]["wallList"].append(item)
            drawing["wallList"].append({"resource": 2, "code": "bearing"})

        # 点
        for item in cornerList:
            drawing["drawing"]["cornerList"].append(item)

        
    def is_point_on_line_segment(self, point, line_start, line_end):
        """
        判断点是否在线段上
        """
        x, y = point
        x1, y1 = line_start
        x2, y2 = line_end
        # 计算点到线段起点的距离
        dist_start = math.sqrt((x - x1) ** 2 + (y - y1) ** 2)
        # 计算点到线段终点的距离
        dist_end = math.sqrt((x - x2) ** 2 + (y - y2) ** 2)
        # 计算线段的总长度
        dist_line = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

        # 如果点到线段的距离之和等于线段的总长度，则说明点在线段上
        return math.isclose(dist_start + dist_end, dist_line)

    def intersect(self, line1, line2):
        xdiff = (line1[0] - line1[2], line2[0] - line2[2])
        ydiff = (line1[1] - line1[3], line2[1] - line2[3])
    
        def det(a, b):
            return a[0] * b[1] - a[1] * b[0]
    
        div = det(xdiff, ydiff)
        if div == 0:
           return None
    
        d = (det((line1[0], line1[1]), (line1[2], line1[3])), det((line2[0], line2[1]), (line2[2], line2[3])))
        x = det(d, xdiff) / div
        y = det(d, ydiff) / div
        # return x, y
        # 检查交点是否在两条线段的范围内
        if min(line1[0], line1[2]) <= x <= max(line1[0], line1[2]) and min(line1[1], line1[3]) <= y <= max(line1[1], line1[3]) and min(line2[0], line2[2]) <= x <= max(line2[0], line2[2]) and min(line2[1], line2[3]) <= y <= max(line2[1], line2[3]):
            return x, y
        else:
            return None

    def distance(self, point1, point2):
        """
        计算两点间的距离
        """
        x1, y1 = point1
        x2, y2 = point2
        return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    # 获取没有闭合的墙体
    def xy_check_point(self,xy_bboxes):
        x_data =[]
        y_data = []
        for i,bbox in  enumerate(xy_bboxes):
            index, x1, y1, x2, y2 = bbox
            # 如果 x1 = x2，将边界框信息加入 x1_equals_x2_bboxes
            if x1 == x2:
                x_data.append(bbox)
            
            # 如果 y1 = y2，将边界框信息加入 y1_equals_y2_bboxes
            elif y1 == y2:
                y_data.append(bbox)
            pass

        # 找到所有水平线条没有跟纵轴交叉的线条
        y_data_remove = set() 
        for i, line1 in enumerate(y_data):
            is_ok = False
            for j, line2 in enumerate(x_data):
                if  line1[1] == line2[1] and line1[2]>= line2[2] and line1[2]<= line2[4]:
                    for k, line3 in enumerate(x_data):
                        if  line1[3] == line3[1] and line1[2]>= line3[2] and line1[2]<= line3[4]:
                            is_ok = True
            if is_ok==False:
                y_data_remove.add(i)
        y_data_2 = [line for idx, line in enumerate(y_data) if idx in y_data_remove]

        # 找到所有垂直线条没有跟纵轴交叉的线条
        x_data_remove = set() 
        for i, line1 in enumerate(x_data):
            is_ok = False
            for j, line2 in enumerate(y_data):
                if  line1[2] == line2[2] and line1[1]>= line2[1] and line1[1]<= line2[3]:
                    for k, line3 in enumerate(y_data):
                        if  line1[4] == line3[2] and line1[1]>= line3[1] and line1[1]<= line3[3]:
                            is_ok = True
            if is_ok==False:
                x_data_remove.add(i)
        x_data_2 = [line for idx, line in enumerate(x_data) if idx in x_data_remove]

        # print(f"x_data_2==>{x_data_2}")
        # print(f"y_data_2==>{y_data_2}")
        return x_data_2,y_data_2
        
    
    
    def convert_drawing(self, lines):
        #线条转换为墙体 坐标与墙的比例因子是20：
        scale_factor = self.mangoDivideKe
        h = 200
        scaled_lines = [
            (line[0], line[1]*scale_factor, line[2]*scale_factor, line[3]*scale_factor, line[4]*scale_factor) for line in lines
        ]
        
        # print(scaled_lines)
        
        # # 找出所有墙体的墙角列表
        # corners = set()
        # for line in scaled_lines:
        #     corners.add((line[1], line[2]))  # 添加起点
        #     corners.add((line[3], line[4]))  # 添加终点

        # corners = set()
        # for i in range(len(scaled_lines)):
        #     for j in range(i+1, len(scaled_lines)):
        #         intersection = self.intersect(scaled_lines[i][1:], scaled_lines[j][1:])
        #         if intersection:  # 如果存在交叉点
        #             corners.add(intersection)
        
        # corners = list(corners)
        # print(corners)

        corners = set()
        for i in range(len(scaled_lines)):
            for j in range(i+1, len(scaled_lines)):
                intersection = self.intersect(scaled_lines[i][1:], scaled_lines[j][1:])
                if intersection:  # 如果存在交叉点
                    corners.add(intersection)
            pass
        
        # 讲没有闭合的墙体的角加入墙角列表
        x_line,y_line = self.xy_check_point(scaled_lines)
        for i, boxline in  enumerate(x_line+y_line):
            corners.add((boxline[1],boxline[2]))
            corners.add((boxline[3],boxline[4]))

        corners = list(corners)
        # print(corners)
        
        # print(corners)
        
        # 创建一个新的墙体列表，找出被墙角拆分以后的前部墙体，并摘到对应墙角的下标
        all_walls = []
        
        for line in scaled_lines:
            start_corner = (line[1], line[2])
            end_corner = (line[3], line[4])
        
            # 找到所有在这条线段上的墙角
            on_line_corners = [corner for corner in corners if min(start_corner[0], end_corner[0]) <= corner[0] <= max(start_corner[0], end_corner[0]) and min(start_corner[1], end_corner[1]) <= corner[1] <= max(start_corner[1], end_corner[1])]
        
            # 按照距离起点的距离排序
            on_line_corners.sort(key=lambda x: (x[0]-start_corner[0])**2 + (x[1]-start_corner[1])**2)
        
            # 将这条线段在每个墙角处进行拆分
            for i in range(len(on_line_corners)-1):
                start_index = corners.index(on_line_corners[i])
                end_index = corners.index(on_line_corners[i+1])
                all_walls.append((start_index, end_index, h))
        
        # 对每堵墙的起点和终点的索引进行排序，然后进行去重
        unique_all_walls = set(tuple(sorted(wall[:2])) + (wall[2],) for wall in all_walls)
        
        # 将集合转换为列表
        unique_all_walls = list(unique_all_walls)
        
        # print(unique_all_walls)
        
        # 转换墙角列表
        cornerList = [[int(corner[0]), int(corner[1])] for corner in corners]
        
        # 转换墙体列表
        wallList = [[int(wall[0]), int(wall[1]), int(wall[2])] for wall in unique_all_walls]
        
        # 打印结果
        # print("cornerList:", cornerList)
        # print("wallList:", wallList)
        return cornerList,wallList

